<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>vue的template到render的处理过程</title>
</head>
<body>
  template 到 render 的过程，其实就是问 vue 编译器的工作原理
  1、vue编译器的概念
  2、编译器的必要性
  3、编译器的工作流程


  1. Vue中有个独特的编译器模块，一般都会以compiler为结尾，我们一般称为“compiler"，它的主要作用是将用户编写的template编译为is中可执行的 render函数。 （）
  
  2．之所以需要这个编译过程是为了便于前端程序员能高效的编写视图模板。相比而言，我们还是更愿意用日丁ML 来编写视图，直观且高效。手写render函数不仅效率底下，而且失去了编译期的优化能力。 （比如vue3中在编译期间会判断视图哪些是静态的，哪些是动态的，编译器只会去处理动态的结果，静态的就直接忽略了，后续再做diff算法的时候就不会去遍历整颗树，而是只会去遍历数组中的动态部分，这样更新的效率就比较高，就是有性能方面的考量）
  
  3．在Vue中编译器会先对template进行解析，这一步称为parse，就是一堆正则表达式的判断，结束之后会得到一个JS对象，转换成一个AST抽象语法树，然后再对AST进行深加工的一个转换过程，这一步成为transform，最后将前面得到的AST生成为JS代码，也就是render函数









  vue编译器原理
  简单来说就是：先将template模版转换成ast抽象语法树，ast再转换成渲染函数render。

  那么什么是是ast抽象语法树呢？

  1.ast抽象语法树
  在template模版和render函数之间有一个中间产物叫做ast抽象语法树。它就是个js对象，它能够描述当前模版的结构信息，跟vnode很类似。注意，ast只是程序运行过程中编译产生的，它跟我们最终程序的运行是没有任何关系的。也就是当这个渲染函数生成之后，ast的生命周期就结束了，不再需要了，而那个虚拟DOM则伴随整个程序的生命周期。这个就是ast和虚拟DOM的本质区别。

  2.为什么需要ast呢
  在ast转换成render函数的过程中，需要进行特别的操作。第一次，将template转成的ast是个非常粗糙的js对象，是一次非常粗糙的转换，类似正则表达式的匹配，然后我们的template模版中还有很多表达式，指令，事件需要重新解析，经过这些具体的深加工的解析(transform)之后会得到一个终极ast，然后这个对这个终极ast进行generate，生成render函数 
  
  template => ast => transform => ast => render

  那么vue中的编译发生在什么时候呢？
  这个时候我们就需要进一步了解vue包的不同版本的不同功能了。vue有携带编译器和不携带编译的包（对不同构建版本的解释）。

  3.运行时编译（直接引入的方式）
  在使用携带编译器(compiler)的vue包的时候，vue编译的时刻是发生在挂载($mount)的时候。

  4.运行时不编译（一般我们使用webpack创建的项目，都是这个，是预打包环境）
  如果使用未携带编译器的vue包的时候，vue在运行时是不会进行编译的。那么它的编译又发生在什么时候呢？使用未携带编译器的vue包的时候，需要进行预编译，也就是基于构建工具使用，就是我们平时使用的vue-cli进行构建的项目，就是使用webpack调用vue-loader进行预编译，将所有vue文件，就是SFC，将里面的template模版部分转换成render函数。这样做的好处就是vue的包体积变小了，执行的时候速度更快了，因为不需要进行编译了。


  Vue3编译过程剖析
  挂载的时候先把template编译成render函数，在创建实例之后，直接调用组件实例的render函数创建这个组件的真实DOM，然后继续向下做递归。

  1.vue2.x和vue3.x的编译对比
  Vue2.x 中的 Compile 过程会是这样：
  parse 词法分析，编译模板生成原始粗糙的 AST。
  optimize 优化原始 AST，标记 AST Element 为静态根节点或静态节点。
  generate 根据优化后的 AST，生成可执行代码，例如 _c、_l 之类的。

  在 Vue3 中，整体的 Compile 过程仍然是三个阶段，但是不同于 Vue2.x 的是，第二个阶段换成了正常编译器都会存在的阶段 transform。
  parse 词法分析，编译模板生成原始粗糙的 AST。
  transform 遍历 AST, 对每一个 AST element 进行转化，例如文本元素、指令元素、动态元素等等的转化
  generate 根据优化后的 AST，生成可执行代码函数。



  patchFlag，那么什么是patchFlag呢？

  什么是 patchFlag
  patchFlag 是 complier 时的 transform 阶段解析 AST Element 打上的补丁标记。它会为 runtime 时的 patchVNode 提供依据，从而实现靶向更新 VNode 和静态提升的效果。

  patchFlag 被定义为一个数字枚举类型，它的每一个枚举值对应的标识意义是：
  TEXT = 1  动态文本的元素
  CLASS = 2  动态绑定 class 的元素
  STYLE = 4  动态绑定 style 的元素
  PROPS = 8  动态 props 的元素，且不含有 class、style 绑定
  FULL_PROPS = 16  动态 props 和带有 key 值绑定的元素
  HYDRATE_EVENTS = 32  事件监听的元素
  STABLE_FRAGMENT = 64  子元素的订阅不会改变的 Fragment 元素
  KEYED_FRAGMENT = 128  自己或子元素带有 key 值绑定的 Fragment 元素
  UNKEYED_FRAGMENT = 256  没有 key 值绑定的 Fragment 元素
  NEED_PATCH=512  带有 ref、指令的元素
  DYNAMIC_SLOTS = 1024  动态 slot 的组件元素
  HOISTED = -1  静态的元素
  BAIL = -2  不是 render 函数生成的一些元素，例如 renderSlot

  整体上 patchFlag 的分为两大类：
  当 patchFlag 的值大于 0 时，代表所对应的元素在 patchVNode 时或 render 时是可以被优化生成或更新的
  当 patchFlag 的值小于 0 时，代表所对应的元素在 patchVNode 时，是需要被 full diff，即进行递归遍历 VNode tree 的比较更新过程。
  以上就是vue3的一个非常高效的优化策略叫补丁标记和动态属性记录。
</body>
</html>